package logs

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/json-iterator/go"
	"github.com/pkg/errors"
)

var (
	LogTimeFormat  = "2006-01-02 15:04:05"
	NewTimeFormat  = "2006-01-02T15:04:05-07:00"
	LogTimeZone	   = time.UTC
	NewTimeZone, _ = time.LoadLocation("Asia/Shanghai")
)

type HTTPRequest struct {
	Time        string                         `json:"time"`
	Service     string                         `json:"service"`
	Environment string                         `json:"environment"`
	IP          string                         `json:"ip"`
	User        string                         `json:"user"`
	Method      string                         `json:"method"`
	Path        string                         `json:"path"`
	Headers     map[string]jsoniter.RawMessage `json:"headers"`
	Get         map[string]jsoniter.RawMessage `json:"get"`
	Post        map[string]jsoniter.RawMessage `json:"post"`
	Extra       map[string]jsoniter.RawMessage `json:"extra"`
	Original    []byte
}

func (l HTTPRequest) GetPostParam(key string) string {
	if value, ok := l.Post[key]; ok {
		return strings.Trim(string(value), `"`)
	}
	return ""
}

func (l HTTPRequest) LogTime() (time.Time, error) {
	return time.ParseInLocation(NewTimeFormat, l.Time, NewTimeZone)
}

func Parse(data []byte) (HTTPRequest, error) {
	ss := strings.Split(string(data), "\t")
	if len(ss) < 8 {
		return HTTPRequest{}, errors.WithStack(fmt.Errorf("parse size let 8"))
	}

	t, err := ParseTime(ss[0])
	if err != nil {
		return HTTPRequest{}, err
	}

	r := HTTPRequest{
		Time:        t.Format(NewTimeFormat),
		Environment: environment(string(data)),
		Service:     "party",
		IP:          ss[1],
		User:        ss[2],
		Method:      ss[3],
		Path:        ss[4],
		Headers:     make(map[string]jsoniter.RawMessage),
		Get:         make(map[string]jsoniter.RawMessage),
		Post:        make(map[string]jsoniter.RawMessage),
		Extra:       make(map[string]jsoniter.RawMessage),
		Original:    data,
	}

	if params := ss[5]; params != "" {
		if err := jsoniter.UnmarshalFromString(params, &r.Headers); err != nil {
			return HTTPRequest{}, errors.Wrap(err, "json unmarshal headers")
		}
	}

	if params := ss[6]; params != "" && params != "[]" {
		if err := jsoniter.UnmarshalFromString(params, &r.Get); err != nil {
			return HTTPRequest{}, errors.Wrap(err, "json unmarshal get")
		}
	}

	if params := ss[7]; params != "" && params != "[]" {
		if err := jsoniter.UnmarshalFromString(params, &r.Post); err != nil {
			return HTTPRequest{}, errors.Wrap(err, "json unmarshal post")
		}
	}

	if len(ss) == 13 {
		r.Extra = map[string]jsoniter.RawMessage{
			"bodyParams":   rawMessage(ss[8]),
			"appendData":   rawMessage(ss[9]),
			"errno":        rawMessage(ss[10]),
			"runtime":      rawMessage(ss[11]),
			"responseBody": rawMessage(ss[12]),
		}
	}

	return r, nil
}

func environment(s string) string {
	if s == "" {
		return ""
	}
	suffix := ".chunk.api.request"
	if strings.HasSuffix(s, suffix) {
		environment := s[0 : len(s)-len(suffix)]
		if environment != "" {
			return environment
		}
		return ""
	}
	return ""
}

func ParseTime(s string) (time.Time, error) {
	if s == "" {
		return time.Time{}, errors.New("time is nil")
	}
	ts := strings.Split(s, ": ")
	tt, err := time.ParseInLocation(LogTimeFormat, ts[len(ts)-1], LogTimeZone)
	if err != nil {
		return time.Time{}, errors.WithStack(err)
	}
	return tt.In(NewTimeZone), nil
}

func rawMessage(s string) jsoniter.RawMessage {
	if s == "" {
		return jsoniter.RawMessage(`""`)
	}
	if s[0] == '{' {
		return jsoniter.RawMessage(s)
	}
	s = strconv.Quote(s)
	return jsoniter.RawMessage(s)
}
